/*
 * Copyright (C) 2011-2012 Adam Gregoire <bsdunx@gmail.com>
 * Copyright (C) 2011-2012 Jeff Hubbard <lord2800@gmail.com>
 *
 * This file is part of the ScriptKit scripting engine library.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#ifndef __JSAPI_EXT_HPP__
#define __JSAPI_EXT_HPP__ 1

#include <cstdint>
#include <string>
#include <vector>

#include "jsapi.h"

#include "Exports.hpp"

// Need to decide if these should be RT roots at all and if they should be
// ifdef'ed for release build
#if 0
#define JS_AddRoot(vp) JS_AddNamedRoot(, (vp), NAME(__LINE__, vp))
#define JS_RemoveRoot(vp) JS_RemoveRoot(ScriptEngine::GetRuntime(), (vp));
#define JS_AddObjectRoot(vp) JS_AddNamedObjectRootRT(ScriptEngine::GetRuntime(), (vp), NAME(__LINE__, vp))
#define JS_RemoveObjectRoot(vp) JS_RemoveObjectRootRT(ScriptEngine::GetRuntime(), (vp));
#endif

struct JSClassSpec
{
	JSClass* classp;
	JSClass* proto;
	JSNative ctor;
	unsigned argc;
	JSFunctionSpec* methods;
	JSPropertySpec* properties;
	JSFunctionSpec* static_methods;
	JSPropertySpec* static_properties;
};

struct JSModuleSpec
{
	char* name;
	JSClassSpec* classes;
	JSFunctionSpec* static_methods;
	JSPropertySpec* static_properties;
};

extern "C" SCRIPTKIT_API JSBool JS_ThrowError(JSContext* cx, const char* message, ...);

template <class T> struct Converter;

template <> struct Converter<const jschar*>
{
	jsval operator()(JSContext* cx, const jschar* c) { return STRING_TO_JSVAL(JS_NewUCStringCopyZ(cx, c)); }
	const jschar* operator()(JSContext* cx, jsval v, const jschar*) { return JS_GetStringCharsZ(cx, JSVAL_TO_STRING(v)); }
};

template <> struct Converter<unsigned char>
{
	jsval operator()(JSContext* cx, unsigned char c) { return UINT_TO_JSVAL(c); }
	unsigned char operator()(JSContext* cx, jsval v, unsigned char) { return (unsigned char)JSVAL_TO_INT(v); }
};

template <> struct Converter<char*>
{
	jsval operator()(JSContext* cx, char* c) { return STRING_TO_JSVAL(JS_NewStringCopyZ(cx, c)); }
};

template <> struct Converter<int>
{
	jsval operator()(JSContext* cx, int i) { return INT_TO_JSVAL(i); }
	int operator()(JSContext* cx, jsval v, int) { return JSVAL_TO_INT(v); }
};

template <> struct Converter<double>
{
	jsval operator()(JSContext* cx, double d) { return DOUBLE_TO_JSVAL(d); }
	double operator()(JSContext* cx, jsval v, double) { return JSVAL_TO_DOUBLE(v); }
};

template <> struct Converter<uint32_t>
{
	jsval operator()(JSContext* cx, uint32_t ui) { return UINT_TO_JSVAL(ui); }
	uint32_t operator()(JSContext* cx, jsval v, uint32_t) { return (uint32_t)JSVAL_TO_INT(v); }
};

template <> struct Converter<bool>
{
	jsval operator()(JSContext* cx, bool b) { return BOOLEAN_TO_JSVAL(b); }
	bool operator()(JSContext* cx, jsval v, bool) { return !!JSVAL_TO_BOOLEAN(v); }
};

template<typename T>
inline JSBool JS_ArrayToVector(JSContext* cx, JSObject* arr, std::vector<T>& vec)
{
	JS_ASSERT(arr != nullptr);

	Converter<T> converter;
	if(!JS_IsArrayObject(cx, arr))
		return JS_ThrowError(cx, "Object must be an Array");

	jsuint len = 0;
	JS_GetArrayLength(cx, arr, &len);
	for(jsuint i = 0; i < len; i++)
	{
		jsval el = JSVAL_VOID;
		JS_GetElement(cx, arr, i, &el);
		vec.push_back(converter(cx, el, (T)0));
	}
	return JS_TRUE;
}

template<typename T>
inline JSObject* JS_VectorToArray(JSContext* cx, std::vector<T>& vec)
{
	Converter<T> converter;
	size_t len = vec.size();
	jsval* jsarr = new jsval[len];

	int i = 0;
	for(auto it = vec.begin(), end = vec.end(); it != end; it++, i++)
		jsarr[i] = converter(cx, *it);

	JSObject* arr = JS_NewArrayObject(cx, len, jsarr);
	//if(JS_AddNamedObjectRoot(cx, &arr, "vector to array object") == JS_FALSE)
	//	return nullptr;

	//for(int i = 0; i < len; ++i)
	//	JS_SetElement(cx, arr, i, jsarr[i]);

	//JS_RemoveObjectRoot(cx, &arr);

	return arr;
}

#endif /* __JSAPI_EXT_HPP__ */

